﻿using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using UtilLib;

namespace WCFLib
{
    public class HostHelper<TContract, TService>
    {
        private readonly ServiceHost _serviceHost;
        private readonly Uri _serviceAddress;

        /// <summary>
        /// initialize wcf host
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="bindingType"></param>
        public HostHelper(string ipAddress, HostBindingType bindingType)
        {
            _serviceHost = new ServiceHost(typeof(TService));

            BehaviorIncludeException();

            _serviceAddress = CreateUri(bindingType, ipAddress);

            _serviceHost.AddServiceEndpoint(typeof(TContract),
                CreateBinding(bindingType),
                _serviceAddress);
        }

        /// <summary>
        /// support exception detail in client
        /// </summary>
        private void BehaviorIncludeException()
        {
            var debugBehavior = new ServiceDebugBehavior
            {
                HttpHelpPageEnabled = false,
                IncludeExceptionDetailInFaults = true
            };
            if (_serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>() != null)
            {
                _serviceHost.Description.Behaviors.Remove<ServiceDebugBehavior>();
            }
            _serviceHost.Description.Behaviors.Add(debugBehavior);
        }

        private Uri CreateUri(HostBindingType bindingType, string ipAddress)
        {
            string prefix;
            switch (bindingType)
            {
                case HostBindingType.TCP_BUFFERERD:
                case HostBindingType.TCP_STREAM:
                    prefix = "net.tcp://";
                    break;
                case HostBindingType.NETNAMEDPIPE:
                    prefix = "net.pipe://";
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            return new Uri(new Uri($"{prefix}{ipAddress}"), typeof(TContract).Name);
        }

        /// <summary>
        /// add binding
        /// </summary>
        /// <param name="bindingType"></param>
        private Binding CreateBinding(HostBindingType bindingType)
        {
            switch (bindingType)
            {
                case HostBindingType.NETNAMEDPIPE:
                    return new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
                case HostBindingType.TCP_STREAM:
                    return new NetTcpBinding(SecurityMode.None)
                    {
                        MaxConnections = 1000,
                        TransferMode = TransferMode.Streamed
                    };
                case HostBindingType.TCP_BUFFERERD:
                    return new NetTcpBinding(SecurityMode.None)
                    {
                        MaxConnections = 5000,
                        TransferMode = TransferMode.Buffered
                    };
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        public void Open()
        {
            _serviceHost.Opened += (sender, e) =>
            {
                EventLogger.LogInfo($"HOST [{_serviceAddress.AbsoluteUri}] OPENED");
            };
            _serviceHost.Open();
        }

        public void Close()
        {
            _serviceHost.Closed += (sender, e) =>
            {
                EventLogger.LogInfo($"HOST [{_serviceAddress.AbsoluteUri}] CLOSED");
            };
            _serviceHost.Close();
        }
    }
}
